/**
 * 以puppter的方式直接解析并运行json
 * @author mumulin
 * @email 1058386570@qq.com
 * @date 2025/10
 */

import url from 'url';
import { createRunner, PuppeteerRunnerExtension } from '@puppeteer/replay';
import puppeteer from 'puppeteer';
import base,{
	jump,action_click,action_delay,
	action_jumpIndex,action_reStep 
} from '../lib/base.js'
import loadModule from '../source/loadModule.js';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import data from '../source/Birmingham/testData.js'
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

class Extension extends PuppeteerRunnerExtension {
	// 需要runner.runBeforeAllSteps()手动触发 flow参数来自runBeforeAllSteps
	async beforeAllSteps(flow) {
		await super.beforeAllSteps(flow);
		console.log('starting');
	}

	async beforeEachStep(step, flow) {
		this.currentFrame = base.getContext(step, this.page) || this.page.mainFrame()
		// 1. 检查步骤是否指定了iframe
		// if (step.frame && step.frame.length > 0) {
		// 	const frameIndex = step.frame[0];
		// 	const allFrames = this.page.frames(); // 获取所有 iframe（包括主页面）
		
		// 	if (frameIndex >= 0 && frameIndex < allFrames.length) {
		// 		const targetFrame = allFrames[frameIndex];
		// 		console.log(`切换到 iframe[${frameIndex}]，url: ${targetFrame.url()}`);
		// 		// 后续步骤的选择器查找会优先使用此 iframe
		// 		this.currentFrame = targetFrame;
		// 	} else {
		// 	console.warn(`⚠️ iframe 索引${frameIndex}无效，当前存在${allFrames.length}个iframe`);
		// 	}
		// } else {
		// 	// 没有指定 iframe 时，重置上下文到主页面
		// 	this.currentFrame = this.page.mainFrame();
		// }
			
		await super.beforeEachStep(step, flow);
		console.log('before', step.type, step.selectors?.[0]?.[0], step.value||'');
	}

	async afterEachStep(step, flow) {
		await super.afterEachStep(step, flow);
		console.log('after', step.type);
	}
	// 需要 runner.runAfterAllSteps()手动触发  flow参数来自runBeforeAllSteps
	async afterAllSteps(flow) {
		await super.afterAllSteps(flow);
		console.log('done');
	}
}

/**
 * 运行单个json
 * @param {*} extension 
 */
export async function runPuPPteerStep(extension) {
	const steps = extension.steps
	const browser = await puppeteer.launch({
		headless: false, // 显示浏览器窗口
		slowMo: 50, // 速度
	});
	const page = await browser.newPage();
		
	const runner = await createRunner({
		title:extension.title||'自动填报',
	},new Extension(browser, page, 5000));

	await runner.runBeforeAllSteps();

	for (let index = 0; index < steps.length; index++) {
		const step = JSON.parse(JSON.stringify(steps[index]));
		step.timeout = 30000
		await runner.runStep(step)
	}
	
	await runner.runAfterAllSteps();
}


/**
 * 文件夹批量运行
 * @param {*} fload 文件夹
 * @param {*} steps 批量步骤 下划线分割
 */
export async function runPuPPteerFload(fload, runStep) {
	// 加载所需的运行模块
	const dataWork = await loadModule(fload);
	const useStep = runStep.split("_").map(item => item.trim());
	// 加载步骤map
	const mapFile = path.resolve(__dirname, `../source/${fload}/steps.json`)
	let stepsAll
	try{
		stepsAll = JSON.parse(fs.readFileSync(mapFile, 'utf8'));
	}catch(error){
	    throw error;
	}
	const browser = await puppeteer.launch({
		headless: false, // 显示浏览器窗口
		slowMo: 10, // 速度
		// args: [
			// '--window-position=100,100',    // 窗口位置
			// '--window-size=800,800'        // 窗口大小
		// ]
	});
	const page = await browser.newPage();
	// CDP协议会话和实例id
	const client = await page.createCDPSession();
	const { windowId } = await client.send('Browser.getWindowForTarget', {
		targetId: page._targetId
	});
	// 浏览器进程信息
	const process = browser.process();
	// 启动参数
	const launchArgs = process.spawnargs;

	const runner = await createRunner({
		title:stepsAll.title,
	},new Extension(browser, page, 5000));

	
	await runner.runBeforeAllSteps();

	for(let i = 0; i < useStep.length; i++){
		const stepsName = useStep[i]
		const item = stepsAll.steps[stepsName]
		console.log("开始执行", item.description)
		if(!item){
			throw error;
		}
		const stepFile = path.resolve(__dirname, `../source/${fload}/${item.record}`)
		let stepsItem = []
		try{
			stepsItem = JSON.parse(fs.readFileSync(stepFile, 'utf8')).steps;
		}catch(error){
			throw error;
		}
		const loopCount = dataWork.fieldForRunTimes ? await dataWork.fieldForRunTimes({data, item, stepsName}) : base.getfieldForRunTimes(data, item.fieldForRunTimes);
		for (let loopIndex = 0; loopIndex < loopCount; loopIndex++) {
			console.log(stepsName, '执行次数：', loopIndex+1)

			for (let index = 0; index < stepsItem.length; index++) {
				const step = JSON.parse(JSON.stringify(stepsItem[index]));
				step.timeout = 30000
				console.log('\x1b[36m', stepsName, '\x1b[0m', '第',index, '步');
				if(step.selectors) step.selectors = base.parseSelectors(step.selectors)//选择器统一为数组（被手动添加或修改过的记录被浏览器改为字符串了）
				const usePage = base.getContext(step, page) || page //需要处理是在iframe中执行还是在主页面执行
				const juseWork = await dataWork.run({stepsName, data, step, index, page:usePage, loopIndex});
				if(juseWork?.action == jump) {
					console.log('跳过',step.type, step.selectors?.[0]?.[0]);
					continue; // 可以支持跳过某些小步骤，跳到指定步骤，跳过该大步骤，跳到指定大步骤等
				}else if(juseWork?.action == action_reStep){
					// 重新执行该步骤
					index = 0
				}
				try{
					if(step.type=="setViewport"){
						// 是否使用了无头模式
						const isHeadless = launchArgs.some(arg => 
							arg.includes('--headless') || arg === '--headless'
						);
						if(!isHeadless){
							console.log('优化窗口显示效果')
							// 动态调整窗口
							await client.send('Browser.setWindowBounds', {
								windowId,
								bounds: {
									width: step.width,
									height: step.height+85,
									// left: -7,
									// top: 0
								}
							});
						}
					}
					
					await runner.runStep(step)
				}catch(error){
					console.log("执行出错", error)
					// await browser.close();
					return
				}
			}

		}
	}
	

	
	await runner.runAfterAllSteps();
	await base.delay(3500);
	// await browser.close();
}
